package org.apache.ibatis.binding;

import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;

import org.apache.ibatis.reflection.ExceptionUtil;
import org.apache.ibatis.session.SqlSession;
import org.lora.core.jdbctx.TransactionSynchronizationManager;

/**
 * <p>
 * 类名:MapperProxy
 * </p>
 * 描述:<br>
 * 创建时间:2016年11月23日 下午5:38:12<br>
 * 
 * @author Bladnir@outlook.com<br>
 *         修改历史:<br>
 *         2016年11月23日 Bladnir 创建<br>
 *         2016年11月23日 Bladnir 修改事务创建方式<br>
 *         2016年12月02日 Bladnir 修改事务创建方式<br>
 */
public class MapperProxy<T> implements InvocationHandler, Serializable {

	private static final long serialVersionUID = -6424540398559729838L;
	private SqlSession sqlSession;
	private final Class<T> mapperInterface;
	private final Map<Method, MapperMethod> methodCache;
	private String environmentId = "";

	/**
	 * 构造方法
	 * 
	 * @param sqlSession
	 * @param mapperInterface
	 * @param methodCache
	 * @param sqlSessionTransactionFactory
	 */
	public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
		this.sqlSession = sqlSession;
		this.environmentId = sqlSession.getConfiguration().getEnvironment().getId();
		this.mapperInterface = mapperInterface;
		this.methodCache = methodCache;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		if (Object.class.equals(method.getDeclaringClass())) {
			try {
				return method.invoke(this, args);
			} catch (Throwable t) {
				throw ExceptionUtil.unwrapThrowable(t);
			}
		}

		Object result = null;
		try {
			this.sqlSession = TransactionSynchronizationManager.getSqlSession(environmentId);

			final MapperMethod mapperMethod = cachedMapperMethod(method);
			result = mapperMethod.execute(sqlSession, args);

			TransactionSynchronizationManager.commitTransaction(method);

		} catch (Throwable t) {
			TransactionSynchronizationManager.rollbackTransaction();
			throw t;
		} finally {
			TransactionSynchronizationManager.endTransaction(method);
		}

		return result;
	}

	private MapperMethod cachedMapperMethod(Method method) {
		MapperMethod mapperMethod = methodCache.get(method);
		if (mapperMethod == null) {
			mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
			methodCache.put(method, mapperMethod);
		}
		return mapperMethod;
	}

}
